package com.link.gateway.core.utils;


import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.link.gateway.base.auth.model.Authclient;
import com.link.gateway.base.auth.service.AuthclientService;
import com.link.gateway.base.duty.service.DutyService;
import com.link.gateway.base.dutyinfo.service.AppDutyInfoService;
import com.link.gateway.base.dutyinfo.service.DutyInfoService;
import com.link.gateway.base.urlinfo.model.UrlInfo;
import com.link.gateway.base.user.model.User;
import com.link.gateway.core.comproperty.OtherConstants;
import com.link.gateway.core.comproperty.RunProperties;
import com.link.gateway.core.comproperty.TokenConstants;

import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

/**
 * jvm缓存工具类
 *
 * @author guoshan
 * @date 2019-08-19 20:20
 */
public class CacheUtil {

    /**
     * token缓存静态信息，20分钟缓存刷新一次
     */
    private static LoadingCache<String, User> tokenCache = CacheBuilder.newBuilder().initialCapacity(50)
            .maximumSize(1000).expireAfterWrite(Duration.ofMinutes(20L)).build(new CacheLoader<String, User>() {
                @Override
                public User load(String token) throws Exception {
                    return TokenUtils.verifyToken(token);
                }
            });

    public static void invalidateToken(String token) {
        tokenCache.invalidate(token);
    }

    /**
     * 通过谷歌的缓存插件把token信息缓存起来。不需要每一次都调用认证服务器接口去做验证
     * 规则是，20分钟缓存刷新一次。当缓存失效之后，会调用认证服务器接口进行验证
     *
     * @author guoshan
     * @date 2019-08-20 09:10
     */
    public static User checkAndCacheToken(String key) throws ExecutionException {
        //去掉token前面的bearer 字符串 HuangLJ 2019年07月24日
        String token = TokenUtils.getToken(key);
        return tokenCache.get(token);
    }


    /**
     * 所有url的详细信息，5分钟缓存刷新一次
     */
    private static LoadingCache<String, Map<String, UrlInfo>> allUrlDetailCache = CacheBuilder.newBuilder().initialCapacity(1)
            .maximumSize(5).expireAfterWrite(Duration.ofDays(1L)).build(new CacheLoader<String, Map<String, UrlInfo>>() {
                @Override
                public Map<String, UrlInfo> load(String key) {
                    return RunProperties.getAllUrlDetail();
                }
            });

    /**
     * 通过谷歌的缓存插件把url信息缓存起来。不需要每一次都到redis获取
     * 规则是，5分钟缓存刷新一次。当缓存失效之后，会调用认证服务器接口进行验证
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static Map<String, UrlInfo> getAllUrlDetailFromCache(String key) throws ExecutionException {
        return allUrlDetailCache.get(key);
    }

    /**
     * 刷新所有的url缓存信息
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static void refreshAllUrlDetail() {
        allUrlDetailCache.refresh(TokenConstants.ALL_URL_DETAIL_CACHE_KEY);
    }

    /**
     * 所有url的详细信息，5分钟缓存刷新一次
     */
    private static LoadingCache<String, Map<String, String>> allUrlScopeCache = CacheBuilder.newBuilder().initialCapacity(1)
            .maximumSize(5).expireAfterWrite(Duration.ofDays(1L)).build(new CacheLoader<String, Map<String, String>>() {
                @Override
                public Map<String, String> load(String key) {
                    return RunProperties.getAllUrlScope();
                }
            });

    /**
     * 通过谷歌的缓存插件把url范围信息信息缓存起来。不需要每一次都到redis获取
     * 规则是，5分钟缓存刷新一次。当缓存失效之后，会调用认证服务器接口进行验证
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static Map<String, String> getAllUrlScopeFromCache(String key) throws ExecutionException {
        return allUrlScopeCache.get(key);
    }

    /**
     * 刷新所有的url范围信息
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static void refreshAllUrlScope() {
        allUrlScopeCache.refresh(TokenConstants.ALL_URL_SCOPE_CACHE_KEY);
    }

    /**
     * 所有url的详细信息，5分钟缓存刷新一次
     */
    private static LoadingCache<String, Map<String, String>> defaultUrlCache = CacheBuilder.newBuilder().initialCapacity(1)
            .maximumSize(5).expireAfterWrite(Duration.ofDays(1L)).build(new CacheLoader<String, Map<String, String>>() {
                @Override
                public Map<String, String> load(String key) {
                    return RunProperties.getDefUrl();
                }
            });

    /**
     * 通过谷歌的缓存插件把默认url信息信息缓存起来。不需要每一次都到redis获取
     * 规则是，5分钟缓存刷新一次。当缓存失效之后，会调用认证服务器接口进行验证
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static Map<String, String> getDefaultUrlFromCache(String key) throws ExecutionException {
        return defaultUrlCache.get(key);
    }

    /**
     * 刷新缓存
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static void refreshDefaultUrl() {
        defaultUrlCache.refresh(TokenConstants.ALL_DEFAULT_URL_CACHE_KEY);
    }

    /**
     * 所有的token用户信息详细信息，20分钟缓存刷新一次
     */
    private static LoadingCache<String, Map<String, Object>> modelMapCache = CacheBuilder.newBuilder().initialCapacity(30)
            .maximumSize(500).expireAfterWrite(Duration.ofDays(1L)).build(new CacheLoader<String, Map<String, Object>>() {
                @Override
                public Map<String, Object> load(String key) {
                    return TokenUtils.getModelMapFromRedis(key);
                }
            });

    /**
     * 通过谷歌的缓存插件把token用户信息信息缓存起来。不需要每一次都到redis获取
     * 规则是，5分钟缓存刷新一次。当缓存失效之后，会调用认证服务器接口进行验证
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static Map<String, Object> getModelMapFromCache(String key) throws ExecutionException {
        return modelMapCache.get(key);
    }

    /**
     * 刷新ModelMap
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static void refreshModelMapFromCache(String key) throws ExecutionException {
        modelMapCache.refresh(key);
    }


    /**
     * 所有的参数配置信息详细信息，5分钟缓存刷新一次
     */
    private static LoadingCache<Map<String, String>, String> enterpriseParamCache = CacheBuilder.newBuilder().initialCapacity(1)
            .maximumSize(100).expireAfterWrite(Duration.ofDays(1L)).build(new CacheLoader<Map<String, String>, String>() {
                @Override
                public String load(Map<String, String> key) {
                    String corpId = key.get("corpId");
                    String redisKey =  key.get("redisKey");
                    String cropProperty = RedisUtil.getCropProperty(redisKey, corpId);
                    if (cropProperty == null) {
                        cropProperty = "";
                    }
                    return cropProperty;
                }
            });

    /**
     * 通过谷歌的缓存插件把参数配置信息信息缓存起来。不需要每一次都到redis获取
     * 规则是，5分钟缓存刷新一次。当缓存失效之后，会调用认证服务器接口进行验证
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static String getEnterpriseParamFromCache(Map<String, String> key) throws ExecutionException {
        return enterpriseParamCache.get(key);
    }

    /**
     * 所有的职责用户信息详细信息，5分钟缓存刷新一次
     */
    private static LoadingCache<String, Map<String, List<String>>> dutyUsernameCache = CacheBuilder.newBuilder().initialCapacity(1)
            .maximumSize(2).expireAfterWrite(Duration.ofDays(1L)).build(new CacheLoader<String, Map<String, List<String>>>() {
                @Override
                public Map<String, List<String>> load(String key) throws Exception {
                    if (key.equals(TokenConstants.ALL_APP_DUTY_USERNAME_CACHE_KEY)) {
                        return SpringUtils.getBean(AppDutyInfoService.class).setDutyUsernameToCache();
                    }

                    return SpringUtils.getBean(DutyInfoService.class).setDutyUsernameToCache();
                }
            });

    /**
     * 通过谷歌的缓存插件把职责用户信息信息缓存起来。不需要每一次都到redis获取
     * 规则是，5分钟缓存刷新一次。
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static Map<String, List<String>> getDutyUsernameFromCache(String key) throws ExecutionException {
        return dutyUsernameCache.get(key);
    }

    /**
     * 刷新职责用户信息
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static void refreshDutyUsername() {
        dutyUsernameCache.refresh(TokenConstants.ALL_DUTY_USERNAME_CACHE_KEY);
    }

    /**
     * 所有的职责URL信息详细信息，5分钟缓存刷新一次
     */
    private static LoadingCache<String, List<String>> dutyUrlCache = CacheBuilder.newBuilder().initialCapacity(10)
            .maximumSize(50).expireAfterWrite(Duration.ofDays(10L)).build(new CacheLoader<String, List<String>>() {
                @Override
                public List<String> load(String key) throws Exception {
                    return SpringUtils.getBean(DutyInfoService.class).setDutyUrlToCache(key);
                }
            });

    /**
     * 通过谷歌的缓存插件把职责URL信息信息缓存起来。不需要每一次都到redis获取
     * 规则是，5分钟缓存刷新一次
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static List<String> getDutyUrlFromCache(String key) throws ExecutionException {
        return dutyUrlCache.get(key);
    }

    /**
     * 刷新职责URL信息
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static void refreshDutyUrl(String key) {
        dutyUrlCache.refresh(key);
    }

    /**
     * 所有的职责用户信息详细信息，5分钟缓存刷新一次
     *
     * @author guoshan
     * @date 2019-08-22 12:50
     */
    private static LoadingCache<String, Table<String, String, String>> blackWhiteIp = CacheBuilder.newBuilder().initialCapacity(1)
            .maximumSize(2).expireAfterWrite(Duration.ofDays(1L)).build(new CacheLoader<String, Table<String, String, String>>() {
                @Override
                public Table<String, String, String> load(String key) throws Exception {
                    Map<String, UrlInfo> urlInfoMap = RunProperties.getAllUrlDetail();

                    Table<String, String, String> table = HashBasedTable.create();
                    for (Map.Entry<String, UrlInfo> entry : urlInfoMap.entrySet()) {
                        UrlInfo urlInfo = entry.getValue();
                        String blackIps = urlInfo.getBlackList();
                        String whiteIps = urlInfo.getWhiteList();
                        table.put(urlInfo.getUrl(), OtherConstants.TABLE_BLACK_IP_KEY, blackIps == null ? "" : blackIps);
                        table.put(urlInfo.getUrl(), OtherConstants.TABLE_WHITE_IP_KEY, whiteIps == null ? "" : whiteIps);
                    }

                    return table;
                }
            });

    public static void refreshBlackWhite() {
        blackWhiteIp.refresh(OtherConstants.BLACK_WHITE_IP_CACHE_KEY);
    }

    /**
     * @description  从缓存中获取url对应的黑名单
     *
     * @author guoshan
     * @date 19-8-22 下午1:58
     * @param
     * @return 黑名单列表
     */

    public static String getBlackIpList(String url) throws ExecutionException {

        return   blackWhiteIp.get(OtherConstants.BLACK_WHITE_IP_CACHE_KEY).get(url, OtherConstants.TABLE_BLACK_IP_KEY);
    }

    /**
     * @description  从缓存中获取url对应的白名单
     *
     * @author guoshan
     * @date 19-8-22 下午1:58
     * @param
     * @return 白名单列表
     */
    public static String getWhiteIpList(String url) throws ExecutionException {

        return   blackWhiteIp.get(OtherConstants.BLACK_WHITE_IP_CACHE_KEY).get(url, OtherConstants.TABLE_WHITE_IP_KEY);
    }

    /**
     * 所有的职责用户信息详细信息，5分钟缓存刷新一次
     * @author guoshan
     * @date 2019-08-22 12:50
     */
    private static LoadingCache<Map<String,String>, String> cropPropertyCache = CacheBuilder.newBuilder().initialCapacity(1)
            .maximumSize(2).expireAfterWrite(Duration.ofMinutes(5L)).build(new CacheLoader<Map<String,String>, String>() {
                @Override
                public String load(Map<String,String> cropPropertyMap) throws Exception {
                    String propertyKey = cropPropertyMap.get(OtherConstants.CORP_PROPERTY_KEY);
                    String corpId = cropPropertyMap.get(OtherConstants.CORPID);
                    String cropPropertyValue = RedisUtil.getCropProperty(propertyKey,corpId);
                    return cropPropertyValue != null ? cropPropertyValue : "";
                }
            });

    public static String getCorpPropertyValue(String cropProperty) throws ExecutionException {
        if (StringUtils.isBlank(cropProperty)) {
            return  null;
        }
        Map<String,String> cropPropertyMap = new HashMap<>(2);
        cropPropertyMap.put(OtherConstants.CORP_PROPERTY_KEY,cropProperty);
        cropPropertyMap.put(OtherConstants.CORPID, "0");
        return  cropPropertyCache.get(cropPropertyMap);
    }

    /**
     * 缓存密钥信息
     * @author wangdawei
     * @date 2019-08-20 12:50
     */
    private static LoadingCache<String, Authclient> authclientCache = CacheBuilder.newBuilder().initialCapacity(50)
            .maximumSize(1000).expireAfterWrite(Duration.ofDays(1L)).build(new CacheLoader<String, Authclient>() {
                @Override
                public Authclient load(String key) throws Exception {
                    return SpringUtils.getBean(AuthclientService.class).queryClientByClientId(key);
                }
            });

    /**
     * 通过谷歌的缓存插件把缓存密钥信息缓存起来。不需要每一次都到redis获取
     * 规则是，5分钟缓存刷新一次
     *
     * @author huanglongjian
     * @date 2019-08-20 12:50
     */
    public static Authclient getAuthclientCache(String key) throws ExecutionException {
        return authclientCache.get(key);
    }

    public static void refreshAuthclient(String key) {
        authclientCache.refresh(key);
    }

    public static void refreshAppDutyUrl() {
        dutyUrlCache.refresh(TokenConstants.ALL_APP_DUTY_URL_CACHE_KEY);
    }

    public static void refreshAppDutyUsername() {
        dutyUsernameCache.refresh(TokenConstants.ALL_APP_DUTY_USERNAME_CACHE_KEY);
    }

    /**
     * 存储client对应的职责列表
     *
     * @author huanglongjian
     * @date 2020年07月07日
     */
    private static LoadingCache<String, List<Long>> clientDutyCache = CacheBuilder.newBuilder().initialCapacity(5)
            .maximumSize(20).expireAfterWrite(Duration.ofDays(1L)).build(new CacheLoader<String, List<Long>>() {
                @Override
                public List<Long> load(String key) throws Exception {
                    return SpringUtils.getBean(DutyService.class).queryDutyByCientId(key);
                }
            });

    public static void refreshClientDutyCache(String key) {
        clientDutyCache.refresh(key);
    }

    public static List<Long> getClientDutyCache(String key) throws ExecutionException {
        return clientDutyCache.get(key);
    }
}
